package insertionSort;

import lock.ConcreteLock;
import shared.Buffer;
import shared.SortInput;

/**
 * The {@link InputProcessor} class has an input buffer and a result.
 */
public abstract class InputProcessor<T extends Comparable<? super T>> implements Runnable{	
	
	private final Buffer<SortInput<T>> inputBuffer;
	private InputProcessorState<T> state;
	private final lock.Lock lock = new ConcreteLock(true);
	private T result;
	private boolean done;
	
	public Buffer<SortInput<T>> getInputBuffer() {
		return inputBuffer;
	}
	public InputProcessorState<T> getState() {
		return state;
	}
	public void setState(final InputProcessorState<T> state) {
		this.state = state;
	}
	
	/**
	 * Returns the result of this processor.
	 * Locks until a result is available when this processor has no result.
	 * @return the result of this processor.
	 */
	public T getResult() {
		this.getLock().lock();
		final T result = this.result;
		this.getLock().unlock();
		return result;
	}

	public void setResult(final T result){
		this.result = result;
		this.getLock().unlock();
	}
	
	public lock.Lock getLock() {
		return lock;
	}
	public boolean isDone() {
		return done;
	}

	public void setDone() {
		this.done = true;
	}
	
	/**
	 * Constructor of {@link InputProcessor}.
	 */
	public InputProcessor() {
		this.setState(new ReceivedNoInput<T>());
		this.inputBuffer = new Buffer<SortInput<T>>();
	}
	/**
	 * When the state of this processor is {@link ReceivedInput}:
	 * sets the result of this processor to the input in {@link ReceivedInput}.
	 * When the state of this processor is {@link ReceivedNoInput}:
	 * throws an error.
	 */
	protected void setResultFromState() {
		this.getState().accept(new InputProcessorStateVisitor<T>() {

			@Override
			public void visit(final ReceivedInput<T> receivedInput) {
				InputProcessor.this.setResult(receivedInput.getInput());
			}

			@Override
			public void visit(final ReceivedNoInput<T> receivedNoInput) {
				throw new Error("Stopped without result.");
			}
		});
		this.setDone();
	}

}